package com.yyw.foodie.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @author 易煜玮
 * @ClassName ServiceLogAspect.java
 * @Description 使用APO记录业务代码执行花费时间
 * @createTime 2021年05月23日 18:36:00
 */
@Aspect
@Component
@Slf4j
public class ServiceLogAspect {

    /**
     * AOP通知：
     * 1. 前置通知：@Before 在方法调用之前执行
     * 2. 后置通知：@After 在方法正常调用之后执行,方法发生了异常就不执行了
     * 3. 环绕通知：@Around 在方法调用之前和之后，都分别可以执行的通知
     * 4. 异常通知：@AfterThrowing 如果在方法调用过程中发生异常，则通知
     * 5. 最终通知：@AfterReturning 在方法调用之后执行,不管有没有发生异常,类似 finally
     */


    /**
     * 记录Service执行时间
     * @param joinPoint 切面对象
     * @return
     * @throws Throwable
     */
    @Around("blockInfo()")
    public Object recordTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("================= 开始执行 {}.{}() 方法 =================",
                joinPoint.getTarget().getClass(),
                joinPoint.getSignature().getName());

        try {

        }finally {

        }
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 执行目标方法
        Object proceed = joinPoint.proceed();
        // 记录结束时间
        long end = System.currentTimeMillis();
        // 计算总耗时
        long result = end - start;

        if (result > 3000) {
            log.error("================= 执行结束,总耗时 {} 毫秒 =================", result);
        } else if (result > 2000) {
            log.warn("================= 执行结束,总耗时 {} 毫秒 =================", result);
        } else {
            log.info("================= 执行结束,总耗时 {} 毫秒 =================", result);
        }

        return proceed;
    }


    /**
     * 异常通知,记录service发送异常信息
     * @param joinPoint 切面对象
     * @param e 异常对象
     * @return
     * 注意: @AfterReturning和抛出异常后通知（@AfterThrowing）的方法中不能使用ProceedingJoinPoint，
     * 要使用JoinPoint
     */
    @AfterThrowing(value = "blockInfo()", throwing = "e")
    public void throwingLog(JoinPoint joinPoint, Exception e) {
        log.error("================= 切入目标方法名：{}.{} 目标方法,异常信息：{} ",
                joinPoint.getTarget().getClass(),
                joinPoint.getSignature().getName(),
                e.getMessage());
    }


    /**
     * 切面表达式：
     * execution 代表所要执行的表达式主体
     * 第一处 * 代表方法返回类型 *代表所有类型
     * 第二处 包名代表aop监控的类所在的包
     * 第三处 .. 代表该包以及其子包下的所有类方法
     * 第四处 * 代表类名，*代表所有类
     * 第五处 *(..) *代表类中的方法名，(..)表示方法中的任何参数
     */
    @Pointcut("execution(* com.yyw.foodie.service..*.*(..))")
    public void blockInfo(){}

}
